import time

import fire
from utils.logger_helper import LoggerHelper
from utils.mysql_helper import MySQLClient

LoggerHelper.init_logger(logger_name="mysql_skip_errors")
logger = LoggerHelper.get_logger(logger_name="mysql_skip_errors")


class SlaveErrorSkipper(object):
    @classmethod
    def _get_error_list(cls, skip_errors=""):
        try:
            error_list = list()
            for error_no in str(skip_errors).split(sep=","):
                if str(error_no).strip() == "":
                    continue
                error_no = int(error_no)
                error_list.append(error_no)
            if len(error_list) == 0:
                logger.warning("未设置错误列表,退出执行")
                exit(1)
        except Exception as ex:
            logger.warning("解析错误列表出现异常,异常为:{}".format(str(ex)))
            exit(1)
        return error_list

    @classmethod
    def get_current_sql_error(cls, mysql_client: MySQLClient):
        sql_script = "SHOW SLAVE STATUS;"
        query_rows = mysql_client.mysql_query(sql_script=sql_script, return_dict=True)
        if len(query_rows) == 0:
            logger.warning("当前实例无复制,退出运行")
            exit(1)
        if len(query_rows) > 1:
            logger.warning("当前实例是多源复制,退出运行")
            exit(1)
        if query_rows[0]["Slave_SQL_Running"] != "No":
            logger.info("复制的SQL线程运行正常,无需修复")
            return 0
        else:
            return int(query_rows[0]["Last_SQL_Errno"])

    @classmethod
    def _skip_one_error(cls, mysql_client: MySQLClient, error_no):
        mysql_cursor = None
        mysql_conn = None
        try:
            mysql_conn = mysql_client.get_server_connection()
            mysql_cursor = mysql_conn.cursor()
            logger.warning("开始跳过当前错误,错误代码:{}".format(error_no))
            mysql_cursor.execute("STOP SLAVE;")
            mysql_cursor.execute("SET GLOBAL sql_slave_skip_counter=1;")
            mysql_cursor.execute("START SLAVE;")
            logger.warning("完成跳过当前错误,错误代码:{}".format(error_no))
        except Exception as ex:
            logger.error("在执行跳过任务过程中出现异常,异常为:{}".format(str(ex)))
            exit(1)
        finally:
            mysql_client.close_connection(mysql_cursor=mysql_cursor, mysql_conn=mysql_conn)

    @classmethod
    def skip_errors(cls, mysql_host="127.0.0.1", mysql_port=3306,
                    mysql_user="root", mysql_password="",
                    skip_times=1000, skip_errors=""):
        database_name = "mysql"
        default_charset = "utf8mb4"
        skip_errors = cls._get_error_list(skip_errors=skip_errors)
        mysql_client = MySQLClient(
            mysql_host=mysql_host, mysql_port=mysql_port, mysql_user=mysql_user, mysql_password=mysql_password,
            database_name=database_name, default_charset=default_charset, connection_timeout=60
        )
        while skip_times > 0:
            error_no = cls.get_current_sql_error(mysql_client=mysql_client)
            if error_no == 0:
                time.sleep(5)
                continue
            if error_no in skip_errors:
                cls._skip_one_error(mysql_client=mysql_client, error_no=error_no)
                skip_times -= 1
                time.sleep(1)
            else:
                logger.warning("当前错误代码{}不在跳过错误列表中,退出执行".format(error_no))
                exit(1)
        logger.warning("跳过次数达到指定次数,退出执行")


if __name__ == '__main__':
    fire.Fire(SlaveErrorSkipper.skip_errors)
